package top.codechap.aop;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @author CodeChap
 * @date 2021-06-17 16:33
 * @description AOP
 *
 * AOP : [动态代理]
 *      面向切面编程,在程序运行期间,动态地将某段代码切入到指定方法,指定位置进行运行的编程方式
 *
 * 1.导入AOP模块
 * 2.定义一个业务逻辑(MathCalculator)
 * 3.定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div方法运行到哪里,并执行
 *      通知方法:
 *          前置通知(@Before): 在 目标方法 运行之前运行
 *          后置通知(@After): 在 目标方法 运行结束之后运行
 *          返回通知(@AfterReturning): 在 目标方法 正常运行并返回之后运行
 *          异常通知(@AfterThrowing): 在 目标方法 运行出现异常之后运行
 *          环绕通知(@Around): 动态代理,手动推进目标方法运行(jointPoint.proceed())
 * 4.给切面类的目标方法,标注何时何地运行
 * 5.将切面类和业务逻辑类(目标方法类),全部加入到容器中
 * 6.告诉spring,哪个是切面类(给切面类上面加上@Aspect注解)
 * 7.给配置类中加 @EnableAspectJAutoProxy 注解,开启基于注解的AOP模式
 *      在 spring 中 , 会有很多的 xxxEnable
 *
 * 三步 :
 *      一) 将业务逻辑组件和切面类都加入到容器中: 告诉spring哪个是切面类(@Aspect)
 *      二) 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行
 *      三) 卡其基于注解的AOP模式: @EnableAspectJAutoProxy
 *
 * (Spring容器的xxx原理: 看这个原理给容器中注册了什么组件,这个组件什么时候工作,这个组件有什么功能)
 * AOP 原理: @EnableAspectJAutoProxy
 *      1.@EnableAspectJAutoProxy 是什么?
 *          @Import(AspectJAutoProxyRegistrar.class): 给容器中导入 AspectJAutoProxyRegistrar.class
 *              利用 AspectJAutoProxyRegistrar 自定义, 给容器中注册 bean;
 *                  internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator (internalAutoProxyCreator是bean的名字,AnnotationAwareAspectJAutoProxyCreator是bean的类型)
 *          给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator 组件(这个组件本质上就是一个后置处理器);
 *
 *      2.AnnotationAwareAspectJAutoProxyCreator组件 有什么功能?
 *          AnnotationAwareAspectJAutoProxyCreator
 *              -> AspectJAwareAdvisorAutoProxyCreator
 *                  -> AbstractAdvisorAutoProxyCreator
 *                      -> AbstractAutoProxyCreator
 *                          implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                          关注: 后置处理器,在bean初始化完成前后做事情(BeanPostProcessor); 自动装配BeanFactory(BeanFactoryAware)
 *
 *          AbstractAutoProxyCreator.setBeanFactory()   装配 BeanFactory
 *          AbstractAutoProxyCreator.postProcessBeforeInstantiation()   与后置处理器 相关
 *          AbstractAdvisorAutoProxyCreator.setBeanFactory() -> initBeanFactory()   子类重写了父类 AbstractAutoProxyCreator 中的set方法
 *          AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()    子类重写了父类 AbstractAdvisorAutoProxyCreator 中的init方法
 *
 *          给容器中注册AnnotationAwareAspectJAutoProxyCreator 组件的流程:
 *              1) 传入配置类,创建IOC容器
 *              2) 注册配置类,调用refresh()刷新容器,初始化创建所有注册的bean
 *              3) registerBeanPostProcessors(beanFactory); 注册bean的后置处理器, 来方便拦截bean的创建
 *                      1) 先获取ioc容器已经定义了的, 需要创建对象的, 所有BeanPostProcessor(比如前面的AnnotationAwareAspectJAutoProxyCreator 组件)
 *                      2) 给容器中加别的BeanPostProcessor
 *                      3) 优先注册 实现了PriorityOrdered接口 的BeanPostProcessor;
 *                      4) 再给容器中注册 实现了Ordered接口 的BeanPostProcessor;
 *                      5) 最后注册剩余的 没有实现优先级接口 的BeanPostProcessor;
 *                      6xxx) 注册BeanPostProcessor, 实际上就是创建BeanPostProcessor对象, 保存在容器中;
 *                          创建internalAutoProxyCreator的BeanPostProcessor(即,创建AnnotationAwareAspectJAutoProxyCreator)
 *                              1) 创建Bean的实例
 *                              2) 执行populateBean()方法: 给bean的各种属性赋值
 *                              3) 执行initializeBean()方法: 初始化bean;
 *                                      1) 执行invokeAwareMethods()方法: 处理Aware接口的方法回调
 *                                      2) 执行applyBeanPostProcessorBeforeInitialization()方法: 调用后置处理器的postProcessorBeforeInitialization() 的方法
 *                                      3) 执行invokeInitMethods()方法: 执行自定义的初始化方法(即用户自定义的bean的初始化,销毁等方法)
 *                                      4) 执行applyBeanPostProcessorAfterInitialization()方法: 调用后置处理器的postProcessorAfterInitialization() 的方法
 *                              4) BeanPostProcessor(即AnnotationAwareAspectJAutoProxyCreator)创建成功
 *                      7) 把BeanPostProcessor 注册到BeanFactory中;
 *                          beanFactory.addBeanPostProcessor(postProcessor);
 *
 * ======================以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程=================================
 *
 *                  AnnotationAwareAspectJAutoProxyCreator 继承 InstantiationAwareBeanPostProcessor接口,所以其内部的前置处理器是 InstantiationAwareBeanPostProcessor 类型
 *                  因此 AnnotationAwareAspectJAutoProxyCreator 在所有bean创建之前会有一个拦截, 会调用其父类 AbstractAutoProxyCreator 中的postProcessorBeforeInitialization()方法
 *
 *              4) registerBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化工作,创建剩下的单实例Bean(有一部分的Bean在创建注册BeanPostProcessor的时候已经被创建了)
 *                      1) 便利获取容器中全部的Bean, 依次创建对象getBeanName(beanName);
 *                              getBean() -> doGetBean -> getSingleton() ->
 *                      2) 创建Bean
 *                              1) 先从缓存中获取当前的Bean,如果能获取到,说明Bean在之前已经被创建过,直接使用; 否则再创建
 *                                  只要创建好的Bean都会被缓存起来
 *                              2) 再执行createBean()方法, 创建Bean
 *                                      1) 执行resolveBeforeInstantiation(beanName,mbdToUse)方法: 解析BeforeInstantiation
 *                                          希望后置处理器在此能返回一个代理对象; 如果能返回代理对象,则直接使用; 如果不能则向下继续执行
 *                                          1) 后置处理器先尝试返回对象:
 *                                              bean = applyBeanPostProcessorBeforeInitialization();
 *                                                  在这个方法内,拿到全部的后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行后置处理器的postProcessorBeforeInitialization()方法
 *                                              if (bean != null) {
 *                                                  bean = applyBeanPostProcessorAfterInitialization(bean,);
 *                                              }
 *                                              所以 :
 *                                                  %[ BeanPostProcessor是在Bean对象创建初始化前后执行调用的 ]%
 *                                                  %[ InstantiationAwareBeanPostProcessor是在创建Bean实例之前,尝试调用后置处理器返回对象 ]%
 *                                      2) 执行doCreateBean(beanName,mbdToUser,args)方法: 真正的去创建一个bean实例,流程和6xxx是一模一样的
 *
 *          AnnotationAwareAspectJAutoProxyCreator (InstantiationAwareBeanPostProcessor) 的作用:
 *              1) 每一个bean创建之前, 调用postProcessorBeforeInitialization()方法
 *                  关心 MathCalculator 和 LogAspects 的创建
 *                      1) 判断当前bean是否在adviseBeans中 (adviseBeans里面保存了所有需要增强的bean)
 *                      2) 判断当前bean是否是基础类型的 (即这些bean是否实现了 Advice,Pointcut,Advisor,AopInfrastructureBean这些接口 或者 是否是切面(即是否包含@Aspect注解))
 *                      3) 判断当前bean是否需要跳过
 *                              1) 获取候选的增强器(即,切面里面的通知方法,将每个通知方法都包装成了增强器,全部的通知方法就是一个List<Advisor> candidateAdvisors)
 *                                  判断每一个增强器是否是 AspectJPointcutAdvisor, 返回值为true
 *                                  在LogAspects.java中的增强器的类型是 InstantiationModelAwarePointcutAdvisor;
 *                              2) 因此,这里判断的返回值是false
 *             2) 在创建bean对象之后, 调用postProcessorAfterInitialization()方法
 *                方法的返回值是 return wrapIfNecessary(bean,beanName,cacheKey);   //包装如果在需要的情况下
 *                      1) 获取当前bean的所有可用的增强器(通知方法)  将其封装为 Object[] specificIntercepts
 *                              1) 找到候选的全部增强器
 *                              2) 找到能在当前bean使用的增强器(找哪些通知方法是需要切入到当前的bean)
 *                              3) 给增强器排序
 *                      2) 将当前的bean保存adviseBeans中; 表示当前的bean需要增强处理
 *                      3) 如果当前bean需要增强(对应的增强器不为空), 创建当前bean的代理对象;
 *                              1) 获取所有的增强器(通知方法)
 *                              2) 保存到ProxyFactory中
 *                              3) 创建代理对象 (2种) : spring自动决定使用jdk自动代理或者cglib的自动代理
 *                                  JdkDynamicAopProxy(config);
 *                                  ObjenesisCglibAopProxy(config);
 *                      4) 给容器中返回当前bean使用cglib增强了的代理对象
 *                      5) 以后容器中获取到的就是这个bean的代理对象, 执行目标方法的时候, 代理对象就会执行通知方法的流程
 *
 *      3.目标方法的执行 :
 *          容器中保存了组件的代理对象(cglib增强后的对象), 这个对象里面报存了详细信息(比如增强器,目标对象,xxx)
 *              1) 执行CglibProxy.intercept()方法: 拦截目标方法的执行
 *              2) 根据ProxyFactory对象, 获取将要执行的目标方法的拦截器链
 *                  List<Object> chain = this.advised.getInterceptorAndDynamicInterceptionAdvice(method,targetClass)
 *                      1) 创建List<Object> interceptorList 保存所有拦截器, List数组的长度为5
 *                          5 = 1个默认的ExposeInvocationInterceptor增强器 和 4个增强器(自定义的通知方法);
 *                      2) 遍历所有的增强器, 使用register.getInterceptor(advisor), 将其转为Interceptor;
 *                      3) 将全部增强器转为 List<MethodInterceptor> :
 *                          如果是 MethodInterceptor, 则直接加进 List;
 *                          如果不是 则使用 AdvisorAdapt 将增强器转为 MethodInterceptor , 再加入 List
 *                          转换完成后返回 List<MethodInterceptor>
 *
 *              3) 如果没有拦截器链, 直接执行目标方法
 *              4) 如果有拦截器链, 把需要执行的目标对象,目标方法,拦截器链等信息传入 创建好的CglibMethodInvocation对象,并调用proceed()方法
 *              5) 拦截器链的触发过程 :
 *                      1) 如果没有拦截器,或者 拦截器的索引 和 拦截器数组-1 大小一样(说明已经执行到了最后一个拦截器), 则直接执行目标方法
 *                      2) 链式获取每一个拦截器, 拦截器执行invoke方法, 每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
 *                          拦截器链的机制, 保证通知方法与目标方法的执行顺序
 *
 *  总结 :
 *      1) @EnableAspectJAutoProxy 开启 AOP 功能
 *      2) @EnableAspectJAutoProxy 给容器中注册了一个 AnnotationAwareAspectJAutoProxyCreator 组件
 *      3) AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器
 *      4) 容器的创建流程 :
 *              1) registerBeanPostProcessors() 注册后置处理器,创建 AnnotationAwareAspectJAutoProxyCreator 对象
 *              2) finishBeanFactoryInitialization() 初始化剩下的单实例 bean
 *                      1) 创建业务逻辑组件和切面组件
 *                      2) AnnotationAwareAspectJAutoProxyCreator 拦截组件的创建过程
 *                      3) 在组件创建完之后, 判断组件是否需要增强
 *                          是 : 将切面的通知方法, 包装秤增强器(Advisor); 给业务逻辑创建一个代理对象(cglib动态代理)
 *      5) 执行目标方法 :
 *              1) 代理对象执行目标方法
 *              2) Cglib.intercept() :
 *                      1) 得到目标方法的拦截器链(增强器(Advisor)包装成拦截器(MethodInterceptor))
 *                      2) 利用拦截器的链式机制, 依次进入每一个拦截器进行执行
 *                      3) 整个执行效果 :
 *                          正常执行: 前置通知 --> 目标方法 --> 后置通知 --> 返回通知
 *                          异常执行: 前置通知 --> 目标方法 --> 后置通知 --> 异常通知
 *
 */
@EnableAspectJAutoProxy
@Configuration
@ComponentScan(value = "top.codechap.aop")
public class ConfigOfAOP {

}
